package vertx.starter;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpServer;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.SQLConnection;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

public class MainVerticle extends AbstractVerticle {

  JDBCClient dbClient;

  private static final String SQL_CREATE_PAGES_TABLE = "create table if not exists Pages (Id integer identity primary key, Name varchar(255) unique, Content clob)";
  private static final String SQL_GET_PAGE = "select Id, Content from Pages where Name = ?";
  private static final String SQL_CREATE_PAGE = "insert into Pages values (NULL, ?, ?)";
  private static final String SQL_SAVE_PAGE = "update Pages set Content = ? where Id = ?";
  private static final String SQL_ALL_PAGES = "select * from Pages";
  private static final String SQL_DELETE_PAGE = "delete from Pages where Id = ?";

  private static final Logger LOGGER = LoggerFactory.getLogger(MainVerticle.class);

  @Override
  public void start() {
    startHttpServer();
    prepareDatabase();
  }

  private Future<Void> prepareDatabase() {
    Promise<Void> promise = Promise.promise();

    dbClient = JDBCClient.createShared(vertx, new JsonObject()
      .put("url", "jdbc:hsqldb:file:db/wiki")
      .put("driver_class", "org.hsqldb.jdbcDriver")
      .put("max_pool_size", 30));

    dbClient.getConnection(ar -> {
      if (ar.failed()) {
        LOGGER.error("Could not open a database connection", ar.cause());
        promise.fail(ar.cause());
      } else {
        SQLConnection connection = ar.result();
        connection.execute(SQL_CREATE_PAGES_TABLE, create -> {
          connection.close();
          if (create.failed()) {
            LOGGER.error("Database preparation error", create.cause());
            promise.fail(create.cause());
          } else {
            promise.complete();
          }
        });
      }
    });

    return promise.future();
  }


  private Future<Void> startHttpServer() {
    Promise<Void> promise = Promise.promise();
    HttpServer server = vertx.createHttpServer();

    Router router = Router.router(vertx);
    router.get("/page").handler(this::queryHandler);
    router.post().handler(BodyHandler.create());
    router.get("/create").handler(this::pageCreateHandler);

    int port = 8089;
    server
      .requestHandler(router)
      .listen(port, ar -> {
        if (ar.succeeded()) {
          LOGGER.info("HTTP server running on port: " + port);
          promise.complete();
        } else {
          LOGGER.error("Could not start a HTTP server", ar.cause());
          promise.fail(ar.cause());
        }
      });

    return promise.future();
  }


  private void pageCreateHandler(RoutingContext routingContext) {
    dbClient.getConnection(handler -> {
      if (handler.succeeded()) {
        SQLConnection conn = handler.result();
        String name = routingContext.queryParam("name").get(0);
        String content = routingContext.queryParam("content").get(0);

        JsonArray jsonArray = new JsonArray();
        jsonArray.add(name).add(content);
        conn.updateWithParams(SQL_CREATE_PAGE, jsonArray, res -> {
          conn.close();
          if (res.succeeded()) {
          routingContext.response(). end("ok");
            LOGGER.info("Update success");
          } else {
            LOGGER.error("Update failed reason :{}", res);
            routingContext.fail(res.cause());
          }
        });
      }
    });

  }

  private void queryHandler(RoutingContext routingContext) {
    dbClient.getConnection(handler -> {
      if (handler.succeeded()) {
        SQLConnection conn = handler.result();
        conn.query(SQL_ALL_PAGES, res -> {
          conn.close();
          if (res.succeeded()) {
            List<JsonArray> result = res.result().getResults();
            JsonObject data = new JsonObject();
            data.put("data", result);
            routingContext.response().putHeader("Content-Type", "application/json")
              .end(data.toBuffer());
          } else {
            routingContext.fail(res.cause());
          }
        });
      }
    });

  }

}
